iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 27
0
Modern Web

從零開始進入 JavaScript & TypeScript 的世界系列 第 27

第27天-非同步和 Promise (async & await)

  • 分享至 

  • xImage
  •  

就在昨天學習了關於 Promise 之後,似乎對 Promise 好像有點了解了,但是又似乎還是有那麼一點不是很清楚!

今天就要從幾個較為簡單的範例搭配 asyncawait 再重新認識一遍!

Callback Hell

假設我們需要依序執行 5個函式, 並且這5個都需要等待一秒鐘,我們的程式碼就會寫的像下面這樣的 Callback Hell:

const callbackHellRun =
    (do_something_here: {(time: string): void;}) => {
      setTimeout(() => {
        do_something_here('1秒');
        setTimeout(() => {
          do_something_here('2秒');
          setTimeout(() => {
            do_something_here('3秒');
            setTimeout(() => {
              do_something_here('4秒');
              setTimeout(() => {
                do_something_here('5秒');
              }, 1000);
            }, 1000);
          }, 1000);
        }, 1000);
      }, 1000);
    }

callbackHellRun((time: string) => console.log(`Callback Hell: ${time}`));

async & await

這時候為了避免 callback hell,我們將setTimeout的部份改成 Promise 的寫法:

const delay = (second: number) =>
    new Promise(resolve => setTimeout(resolve, second));

由於 Promise 是屬於非同步的, 為了讓他一個接著一個執行,並且我們不要使用 callback 的方式,我們會需要使用 async 讓他知道我們這個函式裡面所執行的有包含非同步的呼叫,並且,在非同步呼叫前面加上 await 告知要等他執行完才能繼續往下。

舉例來說,如果我們這樣寫,他的輸出結果 1秒, 2秒, 3秒, 4秒, 5秒 這五個會同時出現,並且只會花一秒鐘的時間就完成。

const runAsync = (do_something: {(time: string): void}) => {
  delay(1000);
  do_something('1秒');
  delay(1000);
  do_something('2秒');
  delay(1000);
  do_something('3秒');
  delay(1000);
  do_something('4秒');
  delay(1000);
  do_something('5秒');
}

runAsync((time: string) => console.log(time));

但如果我們有使用 async 以及 await,他的輸出結果就會是一次輸出一個, 並且依照 1秒, 2秒, 3秒, 4秒, 5秒的順序一個接著一個,總共花五秒鐘完成。

const runAsync = async (do_something: {(time: string): void}) => {
  await delay(1000);
  do_something('1秒');
  await delay(1000);
  do_something('2秒');
  await delay(1000);
  do_something('3秒');
  await delay(1000);
  do_something('4秒');
  await delay(1000);
  do_something('5秒');
}

runAsync((time: string) => console.log(time));

resolve & reject

還記得昨天有提到,一個 Promise 可能的結果會是成功的 resolve, 也可能是失敗的 reject。並且,在 async 的 function 中,也有可能包含同步或非同步的 function,這些我們全部都可以使用 await 來等他,雖然同步的 function 是不需要使用 await 的,但是,用了也不會怎麼樣。

// 這一行是為了範例使用的,因為我們有一個 Promise 只丟出一個 Error
// 一般來說這樣寫是會有問題的
process.on('unhandledRejection', () => null);


const notAPromise = 123;
const promiseThatWillResolve = new Promise(resolve => {
  setTimeout(() => {
    resolve(456);
  }, 1000);
});
const promiseThatWillReject = new Promise((res, reject) => {
  setTimeout(() => {
    reject(new Error('哎呀!發生錯誤了!我是錯誤訊息!'));
  }, 1000);
});


async function demoAsyncFunction() {
  // 這裡其用不用 await 都沒差
  const res1 = await notAPromise;
  console.log({forNotAPromise: res1});

  const res2 = await promiseThatWillResolve;
  console.log({forPromiseThatWillResolve: res2});

  try {
    // 這裡會丟出一個錯誤, 因為他的結果是 Reject
    const res3 = await promiseThatWillReject;
    console.log('在這個範例中,這行遠遠不會被執行到');
  } catch (e) {
    console.log({forPromiseThatWillReject: e.message});
  }
}
demoAsyncFunction();

上面那個範例的輸出結果就會是:

{ forNotAPromise: 123 }
{ forPromiseThatWillResolve: 456 }
{ forPromiseThatWillReject: '哎呀!發生錯誤了!我是錯誤訊息!' }

上一篇
第26天-Promise
下一篇
第28天-Promise在非同步的應用
系列文
從零開始進入 JavaScript & TypeScript 的世界30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言